package com.zhx.common.common.aspect;


import com.zhx.common.common.annotation.RedissonLock;
import com.zhx.common.common.service.LockService;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import utils.SpElUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;

@Component
@Aspect
@Order(0) // 保证该AOP在@Transactional之前执行,分布式锁在事务外。 加锁-> 开启事务 -> 提交事务 -> 释放锁
public class RedissonLockAspect {
    @Resource
    private LockService lockService;

    /**
     * 使用Redisson的分布式锁对方法进行装饰，实现方法级别的乐观锁。
     *
     * @param joinPoint 待装饰的方法/切入点 注解作用的方法。 @annotation(redissonLock)说明切入点为带有redissonLock注解的方法 比如UserServices.modifyName()
     * @param redissonLock Redisson的分布式锁注解
     * @return 返回装饰后的方法执行结果
     */
    @Around("@annotation(redissonLock)")// 切入点表达式表明这个环绕通知应该应用于带有redissonLock注解的方法。
    public Object around(ProceedingJoinPoint joinPoint , RedissonLock redissonLock) {
        // 获取方法的签名信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取方法对象
        Method method = signature.getMethod();

        // 获取锁前缀，默认为方法名，如果指定了prefix则使用指定的前缀
        String prefix = StringUtils.isBlank(redissonLock.prefix()) ? SpElUtils.getMethodKey(method) : redissonLock.prefix();
        // 解析SpEL表达式，生成锁的key
        String key = SpElUtils.parseSpEl(method,joinPoint.getArgs(),redissonLock.key());

        // 使用锁服务执行带锁的方法，并返回执行结果
        return lockService.executeWithLock(prefix+":"+key,redissonLock.waitTime(),redissonLock.unit(),joinPoint::proceed);
    }

}
